package com.duguo.dynamicmvc.factory.processor.model;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtPrimitiveType;

import com.duguo.dynamicmvc.factory.ControllerFactory;
import com.duguo.dynamicmvc.factory.utils.BeanUtils;
import com.duguo.dynamicmvc.factory.utils.javassist.FieldUtils;
import com.duguo.dynamicmvc.factory.utils.javassist.MethodUtils;

public class ModelFieldCreatorImpl implements ModelFieldCreator{
	    


	public boolean isNumberField(CtClass fieldType) throws Exception{
		if(fieldType.isPrimitive()){
			if(fieldType!=CtClass.booleanType)
				return true;
		}else if(fieldType.isArray() && fieldType.getComponentType().isPrimitive()){
			if(fieldType.getComponentType()!=CtClass.booleanType)
				return true;
		}
		return false;
	}

	public void setupFields(ControllerFactory controllerFactory,CtClass modelImplCtClass,CtMethod controllerMethod,Map<String,Object> staticObjects, ModelBinder modelBinder,ModelAnnotationReader modelAnnotationReader,ModelMethodCreator modelMethodCreator)throws Exception {
        Map<String,CtClass> fields=new LinkedHashMap<String,CtClass>();
        Map<String,CtClass> getters=new LinkedHashMap<String,CtClass>();
        Map<String,CtClass> setters=new LinkedHashMap<String,CtClass>();
        List<CtMethod> allMethod=MethodUtils.findAbstractMethods(modelImplCtClass);
        addAsFields(modelImplCtClass, fields, getters, setters, allMethod);
        modelMethodCreator.overrideModelMethod(modelImplCtClass, fields);
        modelBinder.createBindMethod(controllerFactory,modelImplCtClass,getters,setters,controllerMethod,staticObjects,modelAnnotationReader, this);
    }

	private void addAsFields(CtClass modelImplCtClass, Map<String, CtClass> fields, Map<String, CtClass> getters, Map<String, CtClass> setters, List<CtMethod> allMethod) throws Exception {
		for(CtMethod currentMethod:allMethod){
			if(!currentMethod.getName().startsWith("set"))
				addAsField(modelImplCtClass,currentMethod,fields,getters,setters);
        }
		for(CtMethod currentMethod:allMethod){
			if(currentMethod.getName().startsWith("set"))
				addAsField(modelImplCtClass,currentMethod,fields,getters,setters);
       }
	}



	private void addAsField(CtClass modelImplCtClass, CtMethod currentMethod, Map<String,CtClass> fields,Map<String,CtClass> getters,Map<String,CtClass> setters)throws Exception {
        String fieldName;
        CtClass fieldType;
        if(currentMethod.getReturnType()==CtClass.voidType){
            if(currentMethod.getParameterTypes().length==1){
                fieldType=currentMethod.getParameterTypes()[0];
                fieldName= BeanUtils.getFieldName(currentMethod.getName());
                addField(fieldName, fields, modelImplCtClass, fieldType,setters);
            }else{
            	throw new RuntimeException("the method "+currentMethod.getName()+" has more than one parameter, cannot create as a setter");
            }
        }else{
            if(currentMethod.getParameterTypes().length==0){
                fieldType=currentMethod.getReturnType();
                fieldName= BeanUtils.getFieldName(currentMethod.getName());
                addField(fieldName, fields, modelImplCtClass, fieldType,getters);
            }else{
            	throw new RuntimeException("the method "+currentMethod.getName()+" has parameter, cannot create as a getter");
            }
        }
    }

	private void addField(String fieldName, Map<String,CtClass> fields, CtClass modelImplCtClass, CtClass fieldType,Map<String,CtClass> getterOrSetters) throws Exception {
    	getterOrSetters.put(fieldName, fieldType);
         if(!fields.containsKey(fieldName)){
             fields.put(fieldName,fieldType);
        	 setupModelField(modelImplCtClass,fieldType,fieldName);
         }
    }

	private void setupModelField(CtClass targetCtClass, CtClass fieldCtClass, String fieldName) throws Exception {
    	CtField successCtField  = FieldUtils.createField(targetCtClass, fieldCtClass,"private", fieldName,null);
		if(fieldCtClass.isPrimitive()){
			 CtPrimitiveType fieldType=(CtPrimitiveType)fieldCtClass;
			 FieldUtils.createGetterAndSetter(targetCtClass, successCtField,"this." + fieldName + "=" + fieldName + ";super.put(\"" + fieldName + "\","+fieldType.getWrapperName()+".valueOf("+fieldName+"))");
		}else{
			FieldUtils.createGetterAndSetter(targetCtClass, successCtField,"this." + fieldName + "=" + fieldName + ";if(" + fieldName + "==null) super.remove(\"" + fieldName + "\"); else super.put(\"" + fieldName + "\"," + fieldName + ")");
		}
    }

}
